1. Discovery

This article is to analyze the sales data of a model kit provider. Their product line consists of model ships, planes, cars and military ground vehicles. I will access the data by creating a connection to the database.

As the sales data including the information of customers, orders, products, product lines and the company employees, I will answer below business problems in this article, so that the management could better understand the company’s business status.

  1. How is the sales of each product line?

  2. What’s the ordered quantity and stock quantity of each product?

  3. As we have regional managers, what’s the sales of the employees under each manager?

I will use advanced SQL functions such as subquery, OLAP, window function to answer.

2. Data Preparation

When working with databases, we should always use packages that are DBI-compliant.

The odbc package provides a standard way for us to connect to any database as long as we have an ODBC driver installed. The odbc package is DBI-compliant, and is recommended for ODBC connections in this project.

The plotly package is loaded for data visualization.

2.1 Connect to the Database

# Load the DBI and odbc packages
library(DBI)
library(odbc)

# Verify that odbc recognizes the installed drivers using odbcListDrivers()
sort(unique(odbcListDrivers()[[1]]))
[1] "ODBC Driver 17 for SQL Server"
# check the available driver
odbcListDrivers(
  keep = getOption("odbc.drivers_keep"),
  filter = getOption("odbc.drivers_filter")
)

Only ODBC Driver 17 for SQL Server is available and we will use it for our connection.

# Pass the server address, database name, and the credentials to be used to the connection
con <- DBI::dbConnect(odbc(),
                 Driver = "ODBC Driver 17 for SQL Server",
                 Server = "aseaspdghrtrain.database.windows.net",
                 Database = "asdb-eas-pd-ghrtrain01",
                 UID = "RZ15912",  # use your own userID
                 PWD = "D7!zUi*#", # use your own password
                 PORT = 1433)

# Check the connection
con
<OdbcConnection> RZ15912@aseaspdghrtrain
  Database: asdb-eas-pd-ghrtrain01
  Microsoft SQL Server Version: 12.00.2000

The connection is set up and we can specify the connection for all sql chunks in the following.

# Set up the connection for all sql chunks
knitr::opts_chunk$set(connection = "con")

Now test with a simply query and save the result to a variable.

-- Select all from offices table
SELECT * FROM offices
# Display the returned result
mydata

That worked and we can create the entity relationship diagram (ERD).

2.2 ERD

By checking all the tables in the database, I created below ERD by Lucidchart. The primary key of each table is highlighed in Red. The data type of each column is listed as well. For table orderDetails, it has composite primary keys orderNumber and productCode.

3. Query for the Business Problems

3.1 The sales of each product line

SELECT 
    p.productLine,
    SUM(o.quantityOrdered * o.priceEach) AS totalSales,
    Round (SUM(o.quantityOrdered * o.priceEach) / (SELECT SUM(quantityOrdered * priceEach) FROM orderdetails), 3) AS salesPct,
    SUM(o.quantityOrdered) AS totalQuantity
FROM orderdetails AS o
LEFT JOIN products AS p
ON o.productCode = p.productCode
Group BY p.productLine
ORDER BY 2 DESC;
top_productLine

This table is what we want and we can proceed the data visualization for presentation.

# Plot the total sales percentage of different product lines
fig1 <- plot_ly(data = top_productLine, labels = ~productLine, values = ~totalSales, 
                type = 'pie',
                text = ~totalSales,
                textposition = 'inside',
                textinfo = 'label+percent',
                insidetextfont = list(color = '#FFFFFF'),
                hovertemplate = "%{label}<br>Sales: %{text:,.0f}<br>Percentage: %{percent}<extra></extra>",
                marker = list(colors = brewer.pal(n = 7, name = "Paired"),
                              line = list(color = '#FFFFFF', width = 1)),
                showlegend = FALSE)

fig1 <- fig1 %>% layout(title = 'Total Sales Percentage by Different Product Lines',
         xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

fig1

Beside, we can plot the ordered quantity of each product line.

# Plot the ordered quantity of different product lines
fig2 <- plot_ly(data = top_productLine, y = ~reorder(productLine, totalQuantity), 
                x = ~totalQuantity,
                type = "bar", 
                orientation = "h",
                text = ~totalQuantity,
                texttemplate = "%{x:,}",
                textposition = "auto",
                hovertemplate = "%{label}<br>Ordered Qty: %{text:,}<extra></extra>",
                width = 700,
                height = 400)

fig2 <- fig2 %>% layout(title = "Total Quantity Ordered by Different Product Lines",
                        yaxis = list(title = ""),
                        xaxis = list(title = "", showgrid = FALSE, showticklabels = FALSE))

fig2

Classic cars is the biggest product line by sales and quantity sold, accounting for about 40% of total sales. Vintage cars is the second biggest. Compared to other product lines, Trains is the much smaller product line, only accounting for about 2% of total sales.

3.2 The ordered and stock quantity of each product

We can use below query to generate the quantity sold and in stock for each product.

-- New query with ROLLUP clause, without using JOIN
SELECT 
    pl.productLine,
    p.productName,
    SUM(od.quantityOrdered) AS salesQuantity,
    AVG(p.quantityInStock) AS stockQuantity
FROM 
    productlines AS pl, 
    products AS p, 
    orderdetails AS od
WHERE p.productLine = pl.productLine
AND p.productCode = od.productCode
GROUP BY pl.productLine, p.productName -- Use grouping function
ORDER BY 1, 3 DESC;
top_products

We can see that the company has a catalogue of 109 products. Now let’s make a scatter plot to visualize the table, with a filter function for different procutlines.

fig3 <- plot_ly(data = top_products, x = ~salesQuantity, y = ~stockQuantity, 
               color = ~productLine, colors = "Paired",
               type = "scatter",
               mode = "markers",
               text = ~productName,
               hovertemplate = "%{text}<br>Order Qty: %{x}<br>Stock Qty: %{y}<extra></extra>",
               width = 750,
               height = 450,
               transforms = list(
                                list(
                                  type = 'filter',
                                  target = ~productLine,
                                  operation = '=',
                                  value = unique(top_products$productLine))
               ))

fig3 <- fig3 %>% 
            layout(
              title = "Sales and Stock of Products",
              xaxis = list(title = "Sales Quantity"),
              yaxis = list(title = "Stock Quantity"),
              updatemenus = list(
              list(
                type = "dropdown",
                active = 0,
                yanchor = "bottom",
                buttons = list(
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)),
                       label = "All"),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[1]),
                       label = unique(top_products$productLine)[1]),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[2]),
                       label = unique(top_products$productLine)[2]),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[3]),
                       label = unique(top_products$productLine)[3]),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[4]),
                       label = unique(top_products$productLine)[4]),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[5]),
                       label = unique(top_products$productLine)[5]),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[6]),
                       label = unique(top_products$productLine)[6]),
                  list(method = "restyle",
                       args = list("transforms[0].value", unique(top_products$productLine)[7]),
                       label = unique(top_products$productLine)[7])
              
                                )))
      )

fig3

We see an outlier! By checking the hovertext, we can know it is 1992 Ferrari 360 Spider Red. It is so popular as only this classic car has an ordered quantity over 1800, while all of the other products are sold with a number between 767 and 1111. Besides, we don’t have a product with very low ordered quantity.

By the filter, we can see that the Train product line only has 3 products. This is the contributing factor to its low sales.

In this plot, we can find the products with low stock (less than 200), they are:

  1. 1968 Mustang
  2. 1928 Ford Phaeton Deluxe
  3. 1997 BWM F650 ST
  4. 1960 BSA Gold Star DBD34

3.2 The sales of each sales manager and their subordinates

In employees table, each sales representative has a direct line manager to report to. I will create an SQL View to show the list of each manager and their subordinates.

CREATE VIEW supSub_rz AS
    (
    SELECT
        e2.employeeNumber AS managerID,
        CONCAT(e2.firstName, ' ', e2.lastName) AS managerName,
        e2.jobTitle,
        e1.employeeNumber AS subordinateID,
        CONCAT(e1.firstName, ' ', e1.lastName) AS subordinateName
    FROM employees AS e1, employees AS e2
    WHERE e1.reportsTo = e2.employeeNumber
    ORDER BY 1 OFFSET 0 ROWS
    );
SELECT * FROM supSub_rz

That’s it! The company has 22 employees in total, 1 President, 1 VP Sales. 3 Regional Sales Manager and 1 Sales Rep directly report to the VP Sales. The four guys also have their subordinates reporting to them.

Now let’s write a query with OLAP function ROLLUP to return the sales number of each manager and their subordinates.

SELECT
    s.managerName,
    s.subordinateName,
    SUM(od.quantityOrdered * priceEach) AS sales
FROM supSub_rz AS s, customers AS c, orders AS o, orderdetails AS od
WHERE s.subordinateID= c.salesRepEmployeeNumber
AND c.customerNumber = o.customerNumber
AND o.orderNumber = od.orderNumber
GROUP BY ROLLUP(s.managerName, s.subordinateName)
employee_sales

To have a clearer view for the management, we can add another column to show the sales rank under each sales manager, with window function RANK. According to the job title, we know that each sales manager is responsible for a region, such as NA, EMEA, APAC.

SELECT
    s.managerName,
    s.jobTitle,
    s.subordinateName,
    SUM(od.quantityOrdered * priceEach) AS sales,
    RANK() OVER (PARTITION BY managerName ORDER BY SUM(od.quantityOrdered * priceEach) DESC) AS salesRank
FROM supSub_rz AS s, customers AS c, orders AS o, orderdetails AS od
WHERE s.subordinateID= c.salesRepEmployeeNumber
AND c.customerNumber = o.customerNumber
AND o.orderNumber = od.orderNumber
GROUP BY s.managerName, s.jobTitle, s.subordinateName
employee_rank
# Plot the ordered quantity of different product lines
fig4 <- plot_ly(data = employee_rank, y = ~reorder(subordinateName, sales),
                x = ~sales,
                color = ~managerName,
                type = "bar", 
                orientation = "h",
                text = ~salesRank,
                texttemplate = "%{text}",
                textposition = "auto",
                hovertemplate = "%{label}<br>Sales: %{x:,}<br>Region Rank: %{text}<extra></extra>",
                width = 700,
                height = 400)

fig4 <- fig4 %>% layout(title = "Employee Sales of Different Managers",
                        yaxis = list(title = ""),
                        xaxis = list(title = "", showgrid = TRUE, showticklabels = TRUE),
                        legend = list(x = 0.7, y = 0.2, 
                                      title = list(text = "<b> Line Manager </b>")))

fig4

In this plot, we can clearly see the sales of each sales rep. The sales of each sales rep vary a lot, in the range between 0.3M to over 1.2M. 4 out of the top 5 sales reps are from Gerard’s team (EMEA Region), the other 1 is from Anthony’s team (NA Region). The sales reps from William’s team (APAC Region) has median sales number, while Mary, the company’s VP Sales has Mami Nishi, who directly report to her.

4. Conclusion

From the above analysis, we can conclude that:

  1. Classic cars and Vintage cars are the company’s biggest product lines as both have much more sales than others. Trains is the smallest one, largely because of the low number of available products. The company should keep maintaining the good sales of car products. Developing more products in Trains can probably increase the sales of this product line.

  2. The 1992 Ferrari 360 Spider Red is the company’s flagship product. The company can make further analysis on the success of this product. The experience and marketing strategy is possible to be applied to other products. The company should pay attention to the 4 low-stock products, stock replenishment is necessary in case they cannot fulfill orders in the near future.

  3. The company’s top sales reps are almost for the EMEA and NA region. But some sales with the lowest sales number are also in the two regions. The company can further analyze the sales detail of each sales rep to better understand their performance.

LS0tCnRpdGxlOiAiTW9kZWwgS2l0IFNhbGVzIEFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiFbXShodHRwczovL3d3dy53b25kZXJsYW5kbW9kZWxzLmNvbS9tZWRpYS9yZXNvdXJjZXMvU2hvcFBpY3MvV29uZGVybGFuZCUyME1vZGVsJTIwRGVwdC0yJTIwMTItMDUtMTUuanBnKQoKIyAxLiBEaXNjb3ZlcnkKClRoaXMgYXJ0aWNsZSBpcyB0byBhbmFseXplIHRoZSBzYWxlcyBkYXRhIG9mIGEgbW9kZWwga2l0IHByb3ZpZGVyLiBUaGVpciBwcm9kdWN0IGxpbmUgY29uc2lzdHMgb2YgbW9kZWwgc2hpcHMsIHBsYW5lcywgY2FycyBhbmQgbWlsaXRhcnkgZ3JvdW5kIHZlaGljbGVzLiBJIHdpbGwgYWNjZXNzIHRoZSBkYXRhIGJ5IGNyZWF0aW5nIGEgY29ubmVjdGlvbiB0byB0aGUgZGF0YWJhc2UuCgpBcyB0aGUgc2FsZXMgZGF0YSBpbmNsdWRpbmcgdGhlIGluZm9ybWF0aW9uIG9mIGN1c3RvbWVycywgb3JkZXJzLCBwcm9kdWN0cywgcHJvZHVjdCBsaW5lcyBhbmQgdGhlIGNvbXBhbnkgZW1wbG95ZWVzLCBJIHdpbGwgYW5zd2VyICoqYmVsb3cgYnVzaW5lc3MgcHJvYmxlbXMqKiBpbiB0aGlzIGFydGljbGUsIHNvIHRoYXQgdGhlIG1hbmFnZW1lbnQgY291bGQgYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGNvbXBhbnkncyBidXNpbmVzcyBzdGF0dXMuCgoxLiBIb3cgaXMgdGhlIHNhbGVzIG9mIGVhY2ggcHJvZHVjdCBsaW5lPwoKMi4gV2hhdCdzIHRoZSBvcmRlcmVkIHF1YW50aXR5IGFuZCBzdG9jayBxdWFudGl0eSBvZiBlYWNoIHByb2R1Y3Q/CgozLiBBcyB3ZSBoYXZlIHJlZ2lvbmFsIG1hbmFnZXJzLCB3aGF0J3MgdGhlIHNhbGVzIG9mIHRoZSBlbXBsb3llZXMgdW5kZXIgZWFjaCBtYW5hZ2VyPwoKSSB3aWxsIHVzZSBhZHZhbmNlZCBTUUwgZnVuY3Rpb25zIHN1Y2ggYXMgc3VicXVlcnksIE9MQVAsIHdpbmRvdyBmdW5jdGlvbiB0byBhbnN3ZXIuCgojIDIuIERhdGEgUHJlcGFyYXRpb24KCldoZW4gd29ya2luZyB3aXRoIGRhdGFiYXNlcywgd2Ugc2hvdWxkIGFsd2F5cyB1c2UgcGFja2FnZXMgdGhhdCBhcmUgREJJLWNvbXBsaWFudC4KClRoZSBgb2RiY2AgcGFja2FnZSBwcm92aWRlcyBhIHN0YW5kYXJkIHdheSBmb3IgdXMgdG8gY29ubmVjdCB0byBhbnkgZGF0YWJhc2UgYXMgbG9uZyBhcyB3ZSBoYXZlIGFuIE9EQkMgZHJpdmVyIGluc3RhbGxlZC4gVGhlIGBvZGJjYCBwYWNrYWdlIGlzIERCSS1jb21wbGlhbnQsIGFuZCBpcyByZWNvbW1lbmRlZCBmb3IgT0RCQyBjb25uZWN0aW9ucyBpbiB0aGlzIHByb2plY3QuCgpUaGUgYHBsb3RseWAgcGFja2FnZSBpcyBsb2FkZWQgZm9yIGRhdGEgdmlzdWFsaXphdGlvbi4KCiMjIDIuMSBDb25uZWN0IHRvIHRoZSBEYXRhYmFzZQpgYGB7cn0KIyBMb2FkIHRoZSBEQkkgYW5kIG9kYmMgcGFja2FnZXMKbGlicmFyeShEQkkpCmxpYnJhcnkob2RiYykKbGlicmFyeShwbG90bHkpICMgZm9yIHZpc3VhbGl6YXRpb24KCiMgVmVyaWZ5IHRoYXQgb2RiYyByZWNvZ25pemVzIHRoZSBpbnN0YWxsZWQgZHJpdmVycyB1c2luZyBvZGJjTGlzdERyaXZlcnMoKQpzb3J0KHVuaXF1ZShvZGJjTGlzdERyaXZlcnMoKVtbMV1dKSkKYGBgCgpgYGB7cn0KIyBjaGVjayB0aGUgYXZhaWxhYmxlIGRyaXZlcgpvZGJjTGlzdERyaXZlcnMoCiAga2VlcCA9IGdldE9wdGlvbigib2RiYy5kcml2ZXJzX2tlZXAiKSwKICBmaWx0ZXIgPSBnZXRPcHRpb24oIm9kYmMuZHJpdmVyc19maWx0ZXIiKQopCmBgYAoKT25seSBgT0RCQyBEcml2ZXIgMTcgZm9yIFNRTCBTZXJ2ZXJgIGlzIGF2YWlsYWJsZSBhbmQgd2Ugd2lsbCB1c2UgaXQgZm9yIG91ciBjb25uZWN0aW9uLgoKYGBge3J9CiMgUGFzcyB0aGUgc2VydmVyIGFkZHJlc3MsIGRhdGFiYXNlIG5hbWUsIGFuZCB0aGUgY3JlZGVudGlhbHMgdG8gYmUgdXNlZCB0byB0aGUgY29ubmVjdGlvbgpjb24gPC0gREJJOjpkYkNvbm5lY3Qob2RiYygpLAogICAgICAgICAgICAgICAgIERyaXZlciA9ICJPREJDIERyaXZlciAxNyBmb3IgU1FMIFNlcnZlciIsCiAgICAgICAgICAgICAgICAgU2VydmVyID0gImFzZWFzcGRnaHJ0cmFpbi5kYXRhYmFzZS53aW5kb3dzLm5ldCIsCiAgICAgICAgICAgICAgICAgRGF0YWJhc2UgPSAiYXNkYi1lYXMtcGQtZ2hydHJhaW4wMSIsCiAgICAgICAgICAgICAgICAgVUlEID0gIlJaMTU5MTIiLCAgIyB1c2UgeW91ciBvd24gdXNlcklECiAgICAgICAgICAgICAgICAgUFdEID0gIkQ3IXpVaSojIiwgIyB1c2UgeW91ciBvd24gcGFzc3dvcmQKICAgICAgICAgICAgICAgICBQT1JUID0gMTQzMykKCiMgQ2hlY2sgdGhlIGNvbm5lY3Rpb24KY29uCmBgYApUaGUgY29ubmVjdGlvbiBpcyBzZXQgdXAgYW5kIHdlIGNhbiBzcGVjaWZ5IHRoZSBjb25uZWN0aW9uIGZvciBhbGwgc3FsIGNodW5rcyBpbiB0aGUgZm9sbG93aW5nLgoKYGBge3Igc2V0dXB9CiMgU2V0IHVwIHRoZSBjb25uZWN0aW9uIGZvciBhbGwgc3FsIGNodW5rcwprbml0cjo6b3B0c19jaHVuayRzZXQoY29ubmVjdGlvbiA9ICJjb24iKQpgYGAKCk5vdyB0ZXN0IHdpdGggYSBzaW1wbHkgcXVlcnkgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byBhIHZhcmlhYmxlLgpgYGB7c3FsLCBvdXRwdXQudmFyID0gIm15ZGF0YSJ9Ci0tIFNlbGVjdCBhbGwgZnJvbSBvZmZpY2VzIHRhYmxlClNFTEVDVCAqIApGUk9NIG9mZmljZXMKYGBgCgpgYGB7cn0KIyBEaXNwbGF5IHRoZSByZXR1cm5lZCByZXN1bHQKbXlkYXRhCmBgYAoKVGhhdCB3b3JrZWQgYW5kIHdlIGNhbiBjcmVhdGUgdGhlIGBlbnRpdHkgcmVsYXRpb25zaGlwIGRpYWdyYW0gKEVSRClgLgoKIyMgMi4yIEVSRApCeSBjaGVja2luZyBhbGwgdGhlIHRhYmxlcyBpbiB0aGUgZGF0YWJhc2UsIEkgY3JlYXRlZCBiZWxvdyBFUkQgYnkgW0x1Y2lkY2hhcnRdKGh0dHBzOi8vd3d3Lmx1Y2lkY2hhcnQuY29tL3BhZ2VzLykuIFRoZSBwcmltYXJ5IGtleSBvZiBlYWNoIHRhYmxlIGlzIGhpZ2hsaWdoZWQgaW4gKipSZWQqKi4gVGhlIGRhdGEgdHlwZSBvZiBlYWNoIGNvbHVtbiBpcyBsaXN0ZWQgYXMgd2VsbC4gRm9yIHRhYmxlIGBvcmRlckRldGFpbHNgLCBpdCBoYXMgY29tcG9zaXRlIHByaW1hcnkga2V5cyBgb3JkZXJOdW1iZXJgIGFuZCBgcHJvZHVjdENvZGVgLgoKIVtdKGh0dHBzOi8vZ2l0aHViLmNvbS9yaWNreXpoYW5nd2wvZGF0YV9hbmFseXRpY19wcm9qZWN0cy9ibG9iL21hc3Rlci9zcWwvZXJkLnBuZz9yYXc9dHJ1ZSkKCiMgMy4gUXVlcnkgZm9yIHRoZSBCdXNpbmVzcyBQcm9ibGVtcwojIyAzLjEgVGhlIHNhbGVzIG9mIGVhY2ggcHJvZHVjdCBsaW5lCgpgYGB7c3FsLCBvdXRwdXQudmFyID0gInRvcF9wcm9kdWN0TGluZSJ9ClNFTEVDVCAKICAgIHAucHJvZHVjdExpbmUsCiAgICBTVU0oby5xdWFudGl0eU9yZGVyZWQgKiBvLnByaWNlRWFjaCkgQVMgdG90YWxTYWxlcywKICAgIFJvdW5kIChTVU0oby5xdWFudGl0eU9yZGVyZWQgKiBvLnByaWNlRWFjaCkgLyAoU0VMRUNUIFNVTShxdWFudGl0eU9yZGVyZWQgKiBwcmljZUVhY2gpIEZST00gb3JkZXJkZXRhaWxzKSwgMykgQVMgc2FsZXNQY3QsIC0tIFVzZSBzdWJxdWVyeSwgYW5kIHVzZSBSb3VuZCBmdW5jdGlvbiB0byBzZXQgZGVjaW1hbAogICAgU1VNKG8ucXVhbnRpdHlPcmRlcmVkKSBBUyB0b3RhbFF1YW50aXR5CkZST00gb3JkZXJkZXRhaWxzIEFTIG8KTEVGVCBKT0lOIHByb2R1Y3RzIEFTIHAKT04gby5wcm9kdWN0Q29kZSA9IHAucHJvZHVjdENvZGUKR3JvdXAgQlkgcC5wcm9kdWN0TGluZQpPUkRFUiBCWSAyIERFU0M7CmBgYAoKCmBgYHtyfQojIGNoZWNrIHRoZSBxdWVyeSByZXN1bHQKdG9wX3Byb2R1Y3RMaW5lCmBgYAoKVGhpcyB0YWJsZSBpcyB3aGF0IHdlIHdhbnQgYW5kIHdlIGNhbiBwcm9jZWVkIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24gZm9yIHByZXNlbnRhdGlvbi4KCmBgYHtyfQojIFBsb3QgdGhlIHRvdGFsIHNhbGVzIHBlcmNlbnRhZ2Ugb2YgZGlmZmVyZW50IHByb2R1Y3QgbGluZXMKZmlnMSA8LSBwbG90X2x5KGRhdGEgPSB0b3BfcHJvZHVjdExpbmUsIGxhYmVscyA9IH5wcm9kdWN0TGluZSwgdmFsdWVzID0gfnRvdGFsU2FsZXMsIAogICAgICAgICAgICAgICAgdHlwZSA9ICdwaWUnLAogICAgICAgICAgICAgICAgdGV4dCA9IH50b3RhbFNhbGVzLAogICAgICAgICAgICAgICAgdGV4dHBvc2l0aW9uID0gJ2luc2lkZScsCiAgICAgICAgICAgICAgICB0ZXh0aW5mbyA9ICdsYWJlbCtwZXJjZW50JywKICAgICAgICAgICAgICAgIGluc2lkZXRleHRmb250ID0gbGlzdChjb2xvciA9ICcjRkZGRkZGJyksCiAgICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gIiV7bGFiZWx9PGJyPlNhbGVzOiAle3RleHQ6LC4wZn08YnI+UGVyY2VudGFnZTogJXtwZXJjZW50fTxleHRyYT48L2V4dHJhPiIsCiAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9ycyA9IGJyZXdlci5wYWwobiA9IDcsIG5hbWUgPSAiUGFpcmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJyNGRkZGRkYnLCB3aWR0aCA9IDEpKSwKICAgICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBGQUxTRSkKCmZpZzEgPC0gZmlnMSAlPiUgbGF5b3V0KHRpdGxlID0gJ1RvdGFsIFNhbGVzIFBlcmNlbnRhZ2UgYnkgRGlmZmVyZW50IFByb2R1Y3QgTGluZXMnLAogICAgICAgICB4YXhpcyA9IGxpc3Qoc2hvd2dyaWQgPSBGQUxTRSwgemVyb2xpbmUgPSBGQUxTRSwgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSksCiAgICAgICAgIHlheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSkKCmZpZzEKYGBgCgpCZXNpZGUsIHdlIGNhbiBwbG90IHRoZSBvcmRlcmVkIHF1YW50aXR5IG9mIGVhY2ggcHJvZHVjdCBsaW5lLgoKYGBge3J9CiMgUGxvdCB0aGUgb3JkZXJlZCBxdWFudGl0eSBvZiBkaWZmZXJlbnQgcHJvZHVjdCBsaW5lcwpmaWcyIDwtIHBsb3RfbHkoZGF0YSA9IHRvcF9wcm9kdWN0TGluZSwgeSA9IH5yZW9yZGVyKHByb2R1Y3RMaW5lLCB0b3RhbFF1YW50aXR5KSwgCiAgICAgICAgICAgICAgICB4ID0gfnRvdGFsUXVhbnRpdHksCiAgICAgICAgICAgICAgICB0eXBlID0gImJhciIsIAogICAgICAgICAgICAgICAgb3JpZW50YXRpb24gPSAiaCIsCiAgICAgICAgICAgICAgICB0ZXh0ID0gfnRvdGFsUXVhbnRpdHksCiAgICAgICAgICAgICAgICB0ZXh0dGVtcGxhdGUgPSAiJXt4Oix9IiwKICAgICAgICAgICAgICAgIHRleHRwb3NpdGlvbiA9ICJhdXRvIiwKICAgICAgICAgICAgICAgIGhvdmVydGVtcGxhdGUgPSAiJXtsYWJlbH08YnI+T3JkZXJlZCBRdHk6ICV7dGV4dDosfTxleHRyYT48L2V4dHJhPiIsCiAgICAgICAgICAgICAgICB3aWR0aCA9IDcwMCwKICAgICAgICAgICAgICAgIGhlaWdodCA9IDQwMCkKCmZpZzIgPC0gZmlnMiAlPiUgbGF5b3V0KHRpdGxlID0gIlRvdGFsIFF1YW50aXR5IE9yZGVyZWQgYnkgRGlmZmVyZW50IFByb2R1Y3QgTGluZXMiLAogICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICIiLCBzaG93Z3JpZCA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSkKCmZpZzIKYGBgCgoqKkNsYXNzaWMgY2FycyoqIGlzIHRoZSBiaWdnZXN0IHByb2R1Y3QgbGluZSBieSBzYWxlcyBhbmQgcXVhbnRpdHkgc29sZCwgYWNjb3VudGluZyBmb3IgYWJvdXQgNDAlIG9mIHRvdGFsIHNhbGVzLiBWaW50YWdlIGNhcnMgaXMgdGhlIHNlY29uZCBiaWdnZXN0LiBDb21wYXJlZCB0byBvdGhlciBwcm9kdWN0IGxpbmVzLCAqKlRyYWlucyoqIGlzIHRoZSBtdWNoIHNtYWxsZXIgcHJvZHVjdCBsaW5lLCBvbmx5IGFjY291bnRpbmcgZm9yIGFib3V0IDIlIG9mIHRvdGFsIHNhbGVzLgoKIyMgMy4yIFRoZSBvcmRlcmVkIGFuZCBzdG9jayBxdWFudGl0eSBvZiBlYWNoIHByb2R1Y3QKCldlIGNhbiB1c2UgYmVsb3cgcXVlcnkgdG8gZ2VuZXJhdGUgdGhlIHF1YW50aXR5IHNvbGQgYW5kIGluIHN0b2NrIGZvciBlYWNoIHByb2R1Y3QuCgpgYGB7c3FsLCBvdXRwdXQudmFyID0gInRvcF9wcm9kdWN0cyJ9ClNFTEVDVCAKICAgIHBsLnByb2R1Y3RMaW5lLAogICAgcC5wcm9kdWN0TmFtZSwKICAgIFNVTShvZC5xdWFudGl0eU9yZGVyZWQpIEFTIHNhbGVzUXVhbnRpdHksCiAgICBBVkcocC5xdWFudGl0eUluU3RvY2spIEFTIHN0b2NrUXVhbnRpdHkKRlJPTSAKICAgIHByb2R1Y3RsaW5lcyBBUyBwbCwgCiAgICBwcm9kdWN0cyBBUyBwLCAKICAgIG9yZGVyZGV0YWlscyBBUyBvZApXSEVSRSBwLnByb2R1Y3RMaW5lID0gcGwucHJvZHVjdExpbmUKQU5EIHAucHJvZHVjdENvZGUgPSBvZC5wcm9kdWN0Q29kZQpHUk9VUCBCWSBwbC5wcm9kdWN0TGluZSwgcC5wcm9kdWN0TmFtZQpPUkRFUiBCWSAxLCAzIERFU0M7CmBgYAoKYGBge3J9CiMgY2hlY2sgdGhlIHJlc3VsdAp0b3BfcHJvZHVjdHMKYGBgCldlIGNhbiBzZWUgdGhhdCB0aGUgY29tcGFueSBoYXMgYSBjYXRhbG9ndWUgb2YgKioxMDkqKiBwcm9kdWN0cy4gTm93IGxldCdzIG1ha2UgYSBzY2F0dGVyIHBsb3QgdG8gdmlzdWFsaXplIHRoZSB0YWJsZSwgd2l0aCBhIGZpbHRlciBmdW5jdGlvbiBmb3IgZGlmZmVyZW50IHByb2N1dGxpbmVzLgoKYGBge3J9CiMgcGxvdCB0aGUgcHJvZHVjdCBzYWxlcyBhbmQgc3RvY2sKZmlnMyA8LSBwbG90X2x5KGRhdGEgPSB0b3BfcHJvZHVjdHMsIHggPSB+c2FsZXNRdWFudGl0eSwgeSA9IH5zdG9ja1F1YW50aXR5LCAKICAgICAgICAgICAgICAgY29sb3IgPSB+cHJvZHVjdExpbmUsIGNvbG9ycyA9ICJQYWlyZWQiLAogICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLAogICAgICAgICAgICAgICBtb2RlID0gIm1hcmtlcnMiLAogICAgICAgICAgICAgICB0ZXh0ID0gfnByb2R1Y3ROYW1lLAogICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gIiV7dGV4dH08YnI+T3JkZXIgUXR5OiAle3h9PGJyPlN0b2NrIFF0eTogJXt5fTxleHRyYT48L2V4dHJhPiIsCiAgICAgICAgICAgICAgIHdpZHRoID0gNzUwLAogICAgICAgICAgICAgICBoZWlnaHQgPSA0NTAsCiAgICAgICAgICAgICAgICMgYWRkIGEgZmlsdGVyCiAgICAgICAgICAgICAgIHRyYW5zZm9ybXMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gJ2ZpbHRlcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQgPSB+cHJvZHVjdExpbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcGVyYXRpb24gPSAnPScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpKQogICAgICAgICAgICAgICApKQojIGFkZCBkcm9wZG93biBsaXN0IGZvciB0aGUgZmlsdGVyCmZpZzMgPC0gZmlnMyAlPiUgCiAgICAgICAgICAgIGxheW91dCgKICAgICAgICAgICAgICB0aXRsZSA9ICJTYWxlcyBhbmQgU3RvY2sgb2YgUHJvZHVjdHMiLAogICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJTYWxlcyBRdWFudGl0eSIpLAogICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJTdG9jayBRdWFudGl0eSIpLAogICAgICAgICAgICAgIHVwZGF0ZW1lbnVzID0gbGlzdCgKICAgICAgICAgICAgICBsaXN0KAogICAgICAgICAgICAgICAgdHlwZSA9ICJkcm9wZG93biIsCiAgICAgICAgICAgICAgICBhY3RpdmUgPSAwLAogICAgICAgICAgICAgICAgeWFuY2hvciA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgYnV0dG9ucyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLAogICAgICAgICAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KCJ0cmFuc2Zvcm1zWzBdLnZhbHVlIiwgdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSkpLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIkFsbCIpLAogICAgICAgICAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBhcmdzID0gbGlzdCgidHJhbnNmb3Jtc1swXS52YWx1ZSIsIHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzFdKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzFdKSwKICAgICAgICAgICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoInRyYW5zZm9ybXNbMF0udmFsdWUiLCB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVsyXSksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVsyXSksCiAgICAgICAgICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLAogICAgICAgICAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KCJ0cmFuc2Zvcm1zWzBdLnZhbHVlIiwgdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbM10pLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbM10pLAogICAgICAgICAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBhcmdzID0gbGlzdCgidHJhbnNmb3Jtc1swXS52YWx1ZSIsIHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzRdKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzRdKSwKICAgICAgICAgICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoInRyYW5zZm9ybXNbMF0udmFsdWUiLCB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVs1XSksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVs1XSksCiAgICAgICAgICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLAogICAgICAgICAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KCJ0cmFuc2Zvcm1zWzBdLnZhbHVlIiwgdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbNl0pLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbNl0pLAogICAgICAgICAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBhcmdzID0gbGlzdCgidHJhbnNmb3Jtc1swXS52YWx1ZSIsIHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzddKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzddKQogICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKQogICAgICApCgpmaWczCmBgYAoKV2Ugc2VlIGFuIG91dGxpZXIhIEJ5IGNoZWNraW5nIHRoZSBob3ZlcnRleHQsIHdlIGNhbiBrbm93IGl0IGlzICoqMTk5MiBGZXJyYXJpIDM2MCBTcGlkZXIgUmVkKiouIEl0IGlzIHNvIHBvcHVsYXIgYXMgb25seSB0aGlzIGNsYXNzaWMgY2FyIGhhcyBhbiBvcmRlcmVkIHF1YW50aXR5IG92ZXIgMTgwMCwgd2hpbGUgYWxsIG9mIHRoZSBvdGhlciBwcm9kdWN0cyBhcmUgc29sZCB3aXRoIGEgbnVtYmVyIGJldHdlZW4gNzY3IGFuZCAxMTExLiBCZXNpZGVzLCB3ZSBkb24ndCBoYXZlIGEgcHJvZHVjdCB3aXRoIHZlcnkgbG93IG9yZGVyZWQgcXVhbnRpdHkuCgpCeSB0aGUgZmlsdGVyLCB3ZSBjYW4gc2VlIHRoYXQgdGhlICoqVHJhaW4qKiBwcm9kdWN0IGxpbmUgb25seSBoYXMgMyBwcm9kdWN0cy4gVGhpcyBpcyB0aGUgY29udHJpYnV0aW5nIGZhY3RvciB0byBpdHMgbG93IHNhbGVzLgoKSW4gdGhpcyBwbG90LCB3ZSBjYW4gZmluZCB0aGUgcHJvZHVjdHMgd2l0aCAqKmxvdyBzdG9jayoqIChsZXNzIHRoYW4gMjAwKSwgdGhleSBhcmU6IAoKMS4gMTk2OCBNdXN0YW5nCjIuIDE5MjggRm9yZCBQaGFldG9uIERlbHV4ZQozLiAxOTk3IEJXTSBGNjUwIFNUCjQuIDE5NjAgQlNBIEdvbGQgU3RhciBEQkQzNAoKIyMgMy4yIFRoZSBzYWxlcyBvZiBlYWNoIHNhbGVzIG1hbmFnZXIgYW5kIHRoZWlyIHN1Ym9yZGluYXRlcwoKSW4gYGVtcGxveWVlc2AgdGFibGUsIGVhY2ggc2FsZXMgcmVwcmVzZW50YXRpdmUgaGFzIGEgZGlyZWN0IGxpbmUgbWFuYWdlciB0byByZXBvcnQgdG8uIEkgd2lsbCBjcmVhdGUgYW4gU1FMIFZpZXcgdG8gc2hvdyB0aGUgbGlzdCBvZiBlYWNoIG1hbmFnZXIgYW5kIHRoZWlyIHN1Ym9yZGluYXRlcy4KYGBge3NxbH0KLS0gY3JlYXRlIGEgc3FsIHZpZXcKQ1JFQVRFIFZJRVcgc3VwU3ViX3J6IEFTCiAgICAoCiAgICBTRUxFQ1QKICAgICAgICBlMi5lbXBsb3llZU51bWJlciBBUyBtYW5hZ2VySUQsCiAgICAgICAgQ09OQ0FUKGUyLmZpcnN0TmFtZSwgJyAnLCBlMi5sYXN0TmFtZSkgQVMgbWFuYWdlck5hbWUsCiAgICAgICAgZTIuam9iVGl0bGUsCiAgICAgICAgZTEuZW1wbG95ZWVOdW1iZXIgQVMgc3Vib3JkaW5hdGVJRCwKICAgICAgICBDT05DQVQoZTEuZmlyc3ROYW1lLCAnICcsIGUxLmxhc3ROYW1lKSBBUyBzdWJvcmRpbmF0ZU5hbWUKICAgIEZST00gZW1wbG95ZWVzIEFTIGUxLCBlbXBsb3llZXMgQVMgZTIKICAgIFdIRVJFIGUxLnJlcG9ydHNUbyA9IGUyLmVtcGxveWVlTnVtYmVyCiAgICBPUkRFUiBCWSAxIE9GRlNFVCAwIFJPV1MKICAgICk7CmBgYAoKCmBgYHtzcWx9Ci0tIGNoZWNrIHRoZSBjcmVhdGVkIHZpZXcKU0VMRUNUICogCkZST00gc3VwU3ViX3J6CmBgYApUaGF0J3MgaXQhIFRoZSBjb21wYW55IGhhcyAyMiBlbXBsb3llZXMgaW4gdG90YWwsIDEgUHJlc2lkZW50LCAxIFZQIFNhbGVzLiAzIFJlZ2lvbmFsIFNhbGVzIE1hbmFnZXIgYW5kIDEgU2FsZXMgUmVwIGRpcmVjdGx5IHJlcG9ydCB0byB0aGUgVlAgU2FsZXMuIFRoZSBmb3VyIGd1eXMgYWxzbyBoYXZlIHRoZWlyIHN1Ym9yZGluYXRlcyByZXBvcnRpbmcgdG8gdGhlbS4KCk5vdyBsZXQncyB3cml0ZSBhIHF1ZXJ5IHdpdGggT0xBUCBmdW5jdGlvbiBgUk9MTFVQYCB0byByZXR1cm4gdGhlIHNhbGVzIG51bWJlciBvZiBlYWNoIG1hbmFnZXIgYW5kIHRoZWlyIHN1Ym9yZGluYXRlcy4KCmBgYHtzcWwsIG91dHB1dC52YXIgPSAiZW1wbG95ZWVfc2FsZXMifQpTRUxFQ1QKICAgIHMubWFuYWdlck5hbWUsCiAgICBzLnN1Ym9yZGluYXRlTmFtZSwKICAgIFNVTShvZC5xdWFudGl0eU9yZGVyZWQgKiBwcmljZUVhY2gpIEFTIHNhbGVzCkZST00gc3VwU3ViX3J6IEFTIHMsIGN1c3RvbWVycyBBUyBjLCBvcmRlcnMgQVMgbywgb3JkZXJkZXRhaWxzIEFTIG9kCldIRVJFIHMuc3Vib3JkaW5hdGVJRD0gYy5zYWxlc1JlcEVtcGxveWVlTnVtYmVyCkFORCBjLmN1c3RvbWVyTnVtYmVyID0gby5jdXN0b21lck51bWJlcgpBTkQgby5vcmRlck51bWJlciA9IG9kLm9yZGVyTnVtYmVyCkdST1VQIEJZIFJPTExVUChzLm1hbmFnZXJOYW1lLCBzLnN1Ym9yZGluYXRlTmFtZSkgLS0gVXNlIFJPTExVUCBoZXJlCmBgYAoKYGBge3J9CiMgY2hlY2sgdGhlIHJlc3VsdAplbXBsb3llZV9zYWxlcwpgYGAKVG8gaGF2ZSBhIGNsZWFyZXIgdmlldyBmb3IgdGhlIG1hbmFnZW1lbnQsIHdlIGNhbiBhZGQgYW5vdGhlciBjb2x1bW4gdG8gc2hvdyB0aGUgc2FsZXMgcmFuayB1bmRlciBlYWNoIHNhbGVzIG1hbmFnZXIsIHdpdGggd2luZG93IGZ1bmN0aW9uIGBSQU5LYC4gQWNjb3JkaW5nIHRvIHRoZSBqb2IgdGl0bGUsIHdlIGtub3cgdGhhdCBlYWNoIHNhbGVzIG1hbmFnZXIgaXMgcmVzcG9uc2libGUgZm9yIGEgcmVnaW9uLCBzdWNoIGFzIE5BLCBFTUVBLCBBUEFDLgoKYGBge3NxbCwgb3V0cHV0LnZhciA9ICJlbXBsb3llZV9yYW5rIn0KU0VMRUNUCiAgICBzLm1hbmFnZXJOYW1lLAogICAgcy5qb2JUaXRsZSwKICAgIHMuc3Vib3JkaW5hdGVOYW1lLAogICAgU1VNKG9kLnF1YW50aXR5T3JkZXJlZCAqIHByaWNlRWFjaCkgQVMgc2FsZXMsCiAgICBSQU5LKCkgT1ZFUiAoUEFSVElUSU9OIEJZIG1hbmFnZXJOYW1lIE9SREVSIEJZIFNVTShvZC5xdWFudGl0eU9yZGVyZWQgKiBwcmljZUVhY2gpIERFU0MpIEFTIHNhbGVzUmFuayAtLSB3aW5kb3cgZnVuY3Rpb24KRlJPTSBzdXBTdWJfcnogQVMgcywgY3VzdG9tZXJzIEFTIGMsIG9yZGVycyBBUyBvLCBvcmRlcmRldGFpbHMgQVMgb2QKV0hFUkUgcy5zdWJvcmRpbmF0ZUlEPSBjLnNhbGVzUmVwRW1wbG95ZWVOdW1iZXIKQU5EIGMuY3VzdG9tZXJOdW1iZXIgPSBvLmN1c3RvbWVyTnVtYmVyCkFORCBvLm9yZGVyTnVtYmVyID0gb2Qub3JkZXJOdW1iZXIKR1JPVVAgQlkgcy5tYW5hZ2VyTmFtZSwgcy5qb2JUaXRsZSwgcy5zdWJvcmRpbmF0ZU5hbWUKYGBgCgpgYGB7cn0KZW1wbG95ZWVfcmFuawpgYGAKCmBgYHtyfQojIFBsb3QgdGhlIHNhbGVzIG9mIGRpZmZlcmVudCBzYWxlcyByZXBzCmZpZzQgPC0gcGxvdF9seShkYXRhID0gZW1wbG95ZWVfcmFuaywgeSA9IH5yZW9yZGVyKHN1Ym9yZGluYXRlTmFtZSwgc2FsZXMpLAogICAgICAgICAgICAgICAgeCA9IH5zYWxlcywKICAgICAgICAgICAgICAgIGNvbG9yID0gfm1hbmFnZXJOYW1lLAogICAgICAgICAgICAgICAgdHlwZSA9ICJiYXIiLCAKICAgICAgICAgICAgICAgIG9yaWVudGF0aW9uID0gImgiLAogICAgICAgICAgICAgICAgdGV4dCA9IH5zYWxlc1JhbmssCiAgICAgICAgICAgICAgICB0ZXh0dGVtcGxhdGUgPSAiJXt0ZXh0fSIsCiAgICAgICAgICAgICAgICB0ZXh0cG9zaXRpb24gPSAiYXV0byIsCiAgICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gIiV7bGFiZWx9PGJyPlNhbGVzOiAle3g6LH08YnI+UmVnaW9uIFJhbms6ICV7dGV4dH08ZXh0cmE+PC9leHRyYT4iLAogICAgICAgICAgICAgICAgd2lkdGggPSA3MDAsCiAgICAgICAgICAgICAgICBoZWlnaHQgPSA0MDApCgpmaWc0IDwtIGZpZzQgJT4lIGxheW91dCh0aXRsZSA9ICJFbXBsb3llZSBTYWxlcyBvZiBEaWZmZXJlbnQgTWFuYWdlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICIiLCBzaG93Z3JpZCA9IFRSVUUsIHNob3d0aWNrbGFiZWxzID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDAuNywgeSA9IDAuMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IExpbmUgTWFuYWdlciA8L2I+IikpKQoKZmlnNApgYGAKCkluIHRoaXMgcGxvdCwgd2UgY2FuIGNsZWFybHkgc2VlIHRoZSBzYWxlcyBvZiBlYWNoIHNhbGVzIHJlcC4gVGhlIHNhbGVzIG9mIGVhY2ggc2FsZXMgcmVwIHZhcnkgYSBsb3QsIGluIHRoZSByYW5nZSBiZXR3ZWVuIDAuM00gdG8gb3ZlciAxLjJNLiA0IG91dCBvZiB0aGUgdG9wIDUgc2FsZXMgcmVwcyBhcmUgZnJvbSBHZXJhcmQncyB0ZWFtIChFTUVBIFJlZ2lvbiksIHRoZSBvdGhlciAxIGlzIGZyb20gQW50aG9ueSdzIHRlYW0gKE5BIFJlZ2lvbikuIFRoZSBzYWxlcyByZXBzIGZyb20gV2lsbGlhbSdzIHRlYW0gKEFQQUMgUmVnaW9uKSBoYXMgbWVkaWFuIHNhbGVzIG51bWJlciwgd2hpbGUgTWFyeSwgdGhlIGNvbXBhbnkncyBWUCBTYWxlcyBoYXMgTWFtaSBOaXNoaSwgd2hvIGRpcmVjdGx5IHJlcG9ydCB0byBoZXIuCgojIDQuIENvbmNsdXNpb24KCkZyb20gdGhlIGFib3ZlIGFuYWx5c2lzLCB3ZSBjYW4gY29uY2x1ZGUgdGhhdDoKCjEuIENsYXNzaWMgY2FycyBhbmQgVmludGFnZSBjYXJzIGFyZSB0aGUgY29tcGFueSdzIGJpZ2dlc3QgcHJvZHVjdCBsaW5lcyBhcyBib3RoIGhhdmUgbXVjaCBtb3JlIHNhbGVzIHRoYW4gb3RoZXJzLiBUcmFpbnMgaXMgdGhlIHNtYWxsZXN0IG9uZSwgbGFyZ2VseSBiZWNhdXNlIG9mIHRoZSBsb3cgbnVtYmVyIG9mIGF2YWlsYWJsZSBwcm9kdWN0cy4gVGhlIGNvbXBhbnkgc2hvdWxkIGtlZXAgbWFpbnRhaW5pbmcgdGhlIGdvb2Qgc2FsZXMgb2YgY2FyIHByb2R1Y3RzLiBEZXZlbG9waW5nIG1vcmUgcHJvZHVjdHMgaW4gVHJhaW5zIGNhbiBwcm9iYWJseSBpbmNyZWFzZSB0aGUgc2FsZXMgb2YgdGhpcyBwcm9kdWN0IGxpbmUuCgoyLiBUaGUgMTk5MiBGZXJyYXJpIDM2MCBTcGlkZXIgUmVkIGlzIHRoZSBjb21wYW55J3MgZmxhZ3NoaXAgcHJvZHVjdC4gVGhlIGNvbXBhbnkgY2FuIG1ha2UgZnVydGhlciBhbmFseXNpcyBvbiB0aGUgc3VjY2VzcyBvZiB0aGlzIHByb2R1Y3QuIFRoZSBleHBlcmllbmNlIGFuZCBtYXJrZXRpbmcgc3RyYXRlZ3kgaXMgcG9zc2libGUgdG8gYmUgYXBwbGllZCB0byBvdGhlciBwcm9kdWN0cy4gVGhlIGNvbXBhbnkgc2hvdWxkIHBheSBhdHRlbnRpb24gdG8gdGhlIDQgbG93LXN0b2NrIHByb2R1Y3RzLCBzdG9jayByZXBsZW5pc2htZW50IGlzIG5lY2Vzc2FyeSBpbiBjYXNlIHRoZXkgY2Fubm90IGZ1bGZpbGwgb3JkZXJzIGluIHRoZSBuZWFyIGZ1dHVyZS4KCjMuIFRoZSBjb21wYW55J3MgdG9wIHNhbGVzIHJlcHMgYXJlIGFsbW9zdCBmb3IgdGhlIEVNRUEgYW5kIE5BIHJlZ2lvbi4gQnV0IHNvbWUgc2FsZXMgd2l0aCB0aGUgbG93ZXN0IHNhbGVzIG51bWJlciBhcmUgYWxzbyBpbiB0aGUgdHdvIHJlZ2lvbnMuIFRoZSBjb21wYW55IGNhbiBmdXJ0aGVyIGFuYWx5emUgdGhlIHNhbGVzIGRldGFpbCBvZiBlYWNoIHNhbGVzIHJlcCB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGVpciBwZXJmb3JtYW5jZS4KCg==